package com.xinsoft.service.impl;

import cn.hutool.core.collection.CollectionUtil;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.xinsoft.common.exception.BDException;
import com.xinsoft.constant.Const;
import com.xinsoft.entity.dto.InOrOutStock;
import com.xinsoft.entity.dto.ScmProduceInstockDTO;
import com.xinsoft.entity.dto.ScmProduceInstockParam;
import com.xinsoft.entity.po.*;
import com.xinsoft.entity.vo.ScmProduceInstockPageVo;
import com.xinsoft.entity.vo.ScmProduceInstockVo;
import com.xinsoft.mapper.ScmProduceInstockItemMapper;
import com.xinsoft.service.*;
import com.xinsoft.mapper.ScmProduceInstockMapper;
import com.xinsoft.utils.TokenUtil;
import lombok.Synchronized;
import org.apache.commons.lang.StringUtils;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import javax.annotation.Resource;
import java.math.BigDecimal;
import java.util.*;
import java.util.stream.Collectors;

/**
 * @author ZYM
 * @description 针对表【scm_produce_instock(生产入库)】的数据库操作Service实现
 * @createDate 2023-08-14 11:30:37
 */
@Service
public class ScmProduceInstockServiceImpl extends ServiceImpl<ScmProduceInstockMapper, ScmProduceInstock>
        implements ScmProduceInstockService {


    @Resource
    private ScmProduceInstockItemMapper produceInstockItemMapper;

    @Autowired
    private ScmProduceInstockItemService produceInstockItemService;
    @Autowired
    private ScmProduceItemFrozenService produceItemFrozenService;
    @Autowired
    private ScmStockService stockService;
    @Autowired
    private ScmStockHisService stockHisService;
    @Autowired
    private ScmStockFrozenService stockFrozenService;
    @Autowired
    private SysDocumentNumService sysDocumentNumService;

    @Override
    public IPage<ScmProduceInstockPageVo> findPageListByParam(ScmProduceInstockParam param) {
        Page<ScmProduceInstockPageVo> page = new Page<>(param.getCurrentPage(), param.getPageSize());
        IPage<ScmProduceInstockPageVo> pageList = baseMapper.findListByParam(page, param);
        return pageList;
    }

    @Override
    public List<ScmProduceInstockPageVo> findListByParam(ScmProduceInstockParam param) {
        // IPage的参数 size<0 ，临时不分页
        List<ScmProduceInstockPageVo> list = baseMapper.findListByParam(param);
        return list;
    }

    @Override
    @Synchronized
    public Integer tempSave(ScmProduceInstockDTO dto) {
        List<ScmProduceInstockItem> itemList = dto.getScmProduceInstockItemList();
        // 校验本次传入的批次是否重复
        List<String> batchList = itemList.stream().map(ScmProduceInstockItem::getBatch).distinct().collect(Collectors.toList());
        if (batchList.size() != itemList.size()) {
            throw new BDException("批次号不允许重复");
        }
        ScmProduceInstock produceInstock = new ScmProduceInstock();
        BeanUtils.copyProperties(dto, produceInstock);
        produceInstock.setAuditStatus(Const.AUDIT_STATUS_WAIT);
        // 保存或新增
        if (Objects.isNull(produceInstock.getId())) {
            // 更新流水号
            sysDocumentNumService.incrementLastSerialByDocumentId(Const.DOCUMENT_ID.PRODUCE_INSTOCK_DOCUMENT);
        } else {
            List<ScmProduceInstockItem> items = produceInstockItemService.list(Wrappers.lambdaQuery(ScmProduceInstockItem.class).eq(ScmProduceInstockItem::getProduceInstockId, produceInstock.getId()));
            List<Integer> itemIdList = items.stream().map(ScmProduceInstockItem::getId).collect(Collectors.toList());
            produceItemFrozenService.remove(Wrappers.lambdaQuery(ScmProduceItemFrozen.class).eq(ScmProduceItemFrozen::getProduceInstockItemId, itemIdList));
            produceInstockItemService.remove(Wrappers.lambdaQuery(ScmProduceInstockItem.class).eq(ScmProduceInstockItem::getProduceInstockId, produceInstock.getId()));
        }
        // 校验传入的批次是否已经存在
        this.verifyBatchExist(batchList);
        // 保存或更新生产入库
        this.saveOrUpdate(produceInstock);
        // 保存生产入库明细
        itemList.forEach(item -> item.setProduceInstockId(produceInstock.getId()));
        produceInstockItemService.saveBatch(itemList);
        // 保存生产入库冻结明细
        itemList.forEach(item -> {
            List<ScmProduceItemFrozen> itemFrozenList = item.getScmProduceItemFrozenList();
            if (CollectionUtil.isNotEmpty(itemFrozenList)) {
                itemFrozenList.forEach(itemFrozen -> {
                    itemFrozen.setProduceInstockItemId(item.getId());
                });
            }
        });
        List<ScmProduceItemFrozen> itemFrozenList = itemList.stream()
                .map(ScmProduceInstockItem::getScmProduceItemFrozenList)
                .filter(CollectionUtil::isNotEmpty)
                .flatMap(List::stream)
                .collect(Collectors.toList());
        produceItemFrozenService.saveBatch(itemFrozenList);

        return produceInstock.getId();
    }

    /**
     * 校验批次号是否存在
     *
     * @param batchList 待检查的批次列表
     */
    public void verifyBatchExist(List<String> batchList) {
        List<ScmProduceInstockItem> list = produceInstockItemService.list(Wrappers.lambdaQuery(ScmProduceInstockItem.class)
                .in(ScmProduceInstockItem::getBatch, batchList));
        if (list.size() > 0) {
            List<String> batchs = list.stream().map(ScmProduceInstockItem::getBatch).collect(Collectors.toList());
            String str = String.join(",", batchs);
            throw new BDException("批次号[" + str + "]已存在，请重新输入");
        }
        List<ScmStock> stockList = stockService.list(Wrappers.lambdaQuery(ScmStock.class)
                .in(ScmStock::getBatchNo, batchList));
        if (stockList.size() > 0) {
            List<String> batchs = stockList.stream().map(ScmStock::getBatchNo).collect(Collectors.toList());
            throw new BDException("批次号[" + String.join(",", batchs) + "]已存在，请重新输入");
        }
    }

    @Override
    public ScmProduceInstockVo detail(Integer id) {
        ScmProduceInstockVo vo = baseMapper.getProduceInstockById(id);
        vo.setScmProduceInstockItemList(produceInstockItemMapper.listByProduceInstockId(id));
        return vo;
    }

    @Override
    public void del(List<Integer> ids) {
        List<ScmProduceInstock> list = this.list(Wrappers.lambdaQuery(ScmProduceInstock.class)
                .eq(ScmProduceInstock::getAuditStatus, Const.AUDIT_STATUS_NORMAL)
                .in(ScmProduceInstock::getId, ids));
        if (list.size() > 0) {
            throw new BDException("操作失败，只能删除未审核的生产入库单");
        }
        produceInstockItemService.remove(Wrappers.lambdaQuery(ScmProduceInstockItem.class).in(ScmProduceInstockItem::getProduceInstockId, ids));
        this.removeByIds(ids);
    }

    @Override
    public void audit(Integer id) {
        ScmProduceInstock produceInstock = this.getById(id);
        if (Const.AUDIT_STATUS_NORMAL.equals(produceInstock.getAuditStatus())) {
            throw new BDException("该单据已审核，无需重复审核");
        }
        produceInstock.setAuditId(TokenUtil.getUserId());
        produceInstock.setAuditTime(new Date());
        produceInstock.setAuditStatus(Const.AUDIT_STATUS_NORMAL);
        this.updateById(produceInstock);
        // 操作库存数据
        this.inventoryChange(id, produceInstock.getInstockCode(), Const.ChangeType.ADD);
    }

    @Override
    public void reverseAudit(Integer id) {
        ScmProduceInstock produceInstock = this.getById(id);
        if (Const.AUDIT_STATUS_WAIT.equals(produceInstock.getAuditStatus())) {
            throw new BDException("该单据未审核，无需反审核");
        }
        produceInstock.setAuditId(null);
        produceInstock.setAuditTime(null);
        produceInstock.setAuditStatus(Const.AUDIT_STATUS_WAIT);
        this.updateById(produceInstock);
        // 操作库存数据
        this.inventoryChange(id, produceInstock.getInstockCode(), Const.ChangeType.SUBTRACT);
    }

    @Override
    public void saveAudit(ScmProduceInstockDTO dto) {
        // 保存
        Integer id = this.tempSave(dto);
        // 审核
        this.audit(id);
    }

    /**
     * 库存变更
     *
     * @param sourceId   源单id
     * @param sourceCode 源单编码
     * @param changeType 变动类型  0-减少  1-增加
     */
    @Synchronized
    public void inventoryChange(Integer sourceId, String sourceCode, Integer changeType) {
        List<ScmProduceInstockItem> itemList = produceInstockItemService.list(Wrappers.lambdaQuery(ScmProduceInstockItem.class)
                .eq(ScmProduceInstockItem::getProduceInstockId, sourceId));
        List<Integer> itemIdList = itemList.stream().map(ScmProduceInstockItem::getId).collect(Collectors.toList());
        List<ScmProduceItemFrozen> frozenList = produceItemFrozenService.list(Wrappers.lambdaQuery(ScmProduceItemFrozen.class).eq(ScmProduceItemFrozen::getProduceInstockItemId, itemIdList));
        Map<Integer, List<ScmProduceItemFrozen>> frozenListMap = frozenList.stream()
                .collect(Collectors.groupingBy(ScmProduceItemFrozen::getProduceInstockItemId));

        List<ScmStock> stockList = new ArrayList<>();
        List<ScmStockHis> stockHisList = new ArrayList<>();
        itemList.forEach(item -> {
            ScmStock stock = stockService.getOne(Wrappers.lambdaQuery(ScmStock.class)
                    .eq(Objects.nonNull(item.getMaterialId()), ScmStock::getMaterialId, item.getMaterialId())
                    .eq(Objects.nonNull(item.getWarehouseId()), ScmStock::getWarehouseId, item.getWarehouseId())
                    .eq(Objects.nonNull(item.getWarehItemId()), ScmStock::getWarehItemId, item.getWarehItemId())
                    .eq(ScmStock::getStockType, Const.StockType.PRODUCE_INSTOCK)
                    .eq(StringUtils.isNotEmpty(item.getBatch()), ScmStock::getBatchNo, item.getBatch()));

            ScmStockHis stockHis = new ScmStockHis();
            stockHis.setChangeTime(new Date());
            stockHis.setChangePersonId(TokenUtil.getUserId());
            stockHis.setChangeType(changeType);
            stockHis.setSourceDoc(10);
            stockHis.setDocNo(sourceCode);

            stockHis.setBatchNo(item.getBatch());
            stockHis.setWarehouseId(item.getWarehouseId());
            stockHis.setWarehItemId(item.getWarehItemId());
            stockHis.setChangeNum(item.getInstockNum());
            stockHis.setMaterialId(item.getMaterialId());
            // 辅助单位数量=主单位数量*换算系数
            stockHis.setChangeAssistNum(stockHis.getChangeNum().multiply(item.getConvCoefficient()));
            stockHis.setConvCoefficient(item.getConvCoefficient());
            if (Objects.nonNull(stock)) {
                stockHis.setBeforeNum(stock.getBatchNum());
                if (changeType.equals(1)) {
                    stockHis.setAfterNum(stock.getBatchNum().add(item.getInstockNum()));
                    stock.setBatchNum(stock.getBatchNum().add(item.getInstockNum()));
                    // 可用数量 = 可用数量 + 入库数量
                    stock.setBatchNumFree(stock.getBatchNumFree().add(item.getInstockNum()));
                } else {
                    stockHis.setAfterNum(stock.getBatchNum().subtract(item.getInstockNum()));
                    stock.setBatchNum(stock.getBatchNum().subtract(item.getInstockNum()));
                    // 可用数量 = 可用数量 + 入库数量
                    stock.setBatchNumFree(stock.getBatchNumFree().subtract(item.getInstockNum()));
                }

            } else {
                stockHis.setBeforeNum(BigDecimal.ZERO);
                stockHis.setAfterNum(item.getInstockNum());

                stock = new ScmStock();
                stock.setStockCode(sysDocumentNumService.getSysDocumentNumByDocumentId(Const.DOCUMENT_ID.STOCK_DOCUMENT));
                sysDocumentNumService.incrementLastSerialByDocumentId(Const.DOCUMENT_ID.STOCK_DOCUMENT);
                stock.setBatchNum(item.getInstockNum());
                // 可用数量 = 入库数量
                stock.setBatchNumFree(item.getInstockNum());

            }

            // 辅助单位数量=主单位数量*换算系数
            stock.setBatchAssistNum(stock.getBatchNum().multiply(item.getConvCoefficient()));
            stock.setWarehouseId(item.getWarehouseId());
            stock.setWarehItemId(item.getWarehItemId());
            stock.setMaterialId(item.getMaterialId());
            stock.setStockType(Const.StockType.PRODUCE_INSTOCK);
            stock.setBatchNo(item.getBatch());
            stock.setBatchNumFrozen(stock.getBatchNumFrozen() == null ? BigDecimal.ZERO : stock.getBatchNumFrozen());
            stock.setBatchPrice(item.getTaxUnitPrice());
            // stock.setBatchAssistPrice();
            stock.setConvCoefficient(item.getConvCoefficient());

            if (frozenListMap.containsKey(item.getId())) {
                stock.setScmProduceItemFrozenList(frozenListMap.get(item.getId()));
            }

            stockHisList.add(stockHis);
            stockList.add(stock);
        });

        // 保存库存变动信息
        stockService.saveInventoryChange(stockHisList, stockList);
        // 保存或更新冻结信息
        List<ScmStock> updateStockList = new ArrayList<>();
        List<ScmStockFrozen> stockFrozenList = new ArrayList<>();
        if (changeType.equals(Const.ChangeType.ADD)) {
            // 变动类型 是增加，1、将冻结的物料写入冻结表  2、更新库存可用数量和冻结数量
            stockList.forEach(stock -> {
                List<ScmProduceItemFrozen> frozens = stock.getScmProduceItemFrozenList();
                if (CollectionUtil.isNotEmpty(frozens)) {
                    frozens.forEach(frozen -> {
                        ScmStockFrozen stockFrozen = new ScmStockFrozen();
                        stockFrozen.setStockId(stock.getId());
                        stockFrozen.setStockCode(stock.getStockCode());
                        stockFrozen.setWarehouseId(stock.getWarehouseId());
                        stockFrozen.setWarehItemId(stock.getWarehItemId());
                        stockFrozen.setMaterialId(stock.getMaterialId());
                        // stockFrozen.setMaterialNo();
                        stockFrozen.setBatchNo(stock.getBatchNo());
                        stockFrozen.setFrozenComId(frozen.getFrozenComId());
                        // stockFrozen.setBatchProducePlanId();
                        stockFrozen.setBatchNumFrozen(frozen.getBatchNumFrozen());
                        stockFrozenList.add(stockFrozen);
                    });

                    BigDecimal sumBatchNumFrozen = frozens.stream().map(ScmProduceItemFrozen::getBatchNumFrozen).reduce(BigDecimal.ZERO, BigDecimal::add);
                    stock.setBatchNumFree(stock.getBatchNumFree().subtract(sumBatchNumFrozen));
                    stock.setBatchNumFrozen(sumBatchNumFrozen);
                    updateStockList.add(stock);
                }
            });
            // 1.将冻结的物料写入冻结表
            stockFrozenService.saveOrUpdateBatch(stockFrozenList);
        } else {
            // 变动类型 是减少，1、将该生产入库中冻结的物料从库存冻结表中移除  2、更新库存可用数量和冻结数量
            List<ScmProduceItemFrozen> frozens = stockList.stream().map(ScmStock::getScmProduceItemFrozenList).filter(CollectionUtil::isNotEmpty)
                    .flatMap(List::stream).collect(Collectors.toList());
            if (CollectionUtil.isNotEmpty(frozens)) {
                List<Integer> stockIds = stockList.stream().map(ScmStock::getId).distinct().collect(Collectors.toList());
                List<Integer> materialIds = itemList.stream().map(ScmProduceInstockItem::getMaterialId).distinct().collect(Collectors.toList());
                List<Integer> warehouseIds = itemList.stream().map(ScmProduceInstockItem::getWarehouseId).distinct().collect(Collectors.toList());
                List<Integer> warehItemIds = itemList.stream().map(ScmProduceInstockItem::getWarehItemId).distinct().collect(Collectors.toList());
                List<Integer> comIds = frozenList.stream().map(ScmProduceItemFrozen::getFrozenComId).distinct().collect(Collectors.toList());
                // 1.将该生产入库中冻结的物料从库存冻结表中移除
                stockFrozenService.remove(Wrappers.lambdaQuery(ScmStockFrozen.class)
                        .in(ScmStockFrozen::getStockId, stockIds)
                        .in(ScmStockFrozen::getMaterialId, materialIds)
                        .in(ScmStockFrozen::getWarehouseId, warehouseIds)
                        .in(ScmStockFrozen::getWarehItemId, warehItemIds)
                        .in(ScmStockFrozen::getFrozenComId, comIds));

                List<ScmStock> scmStocks = stockService.listByIds(stockIds);
                scmStocks.forEach(scmStock -> {
                    List<ScmProduceItemFrozen> stockFrozens = scmStock.getScmProduceItemFrozenList();
                    BigDecimal sumBatchNumFrozen = stockFrozens.stream().map(ScmProduceItemFrozen::getBatchNumFrozen).reduce(BigDecimal.ZERO, BigDecimal::add);
                    scmStock.setBatchNumFree(scmStock.getBatchNumFree().add(sumBatchNumFrozen));
                    scmStock.setBatchNumFrozen(scmStock.getBatchNumFrozen().subtract(sumBatchNumFrozen));
                    updateStockList.add(scmStock);
                });
            }
        }
        // 2.更新库存可用数量和冻结数量
        stockService.updateBatchById(updateStockList);
    }
}




